package com.bensoft.minesweeper.gui.minesweeperframe;

import com.bensoft.minesweeper.ApplicationController;
import com.bensoft.minesweeper.gui.GUILoader;
import com.bensoft.minesweeper.gui.MinesweeperFrame;
import com.bensoft.minesweeper.gui.minesweeperframe.grid.GridSpace;
import com.bensoft.minesweeper.gui.preferences.ApplicationPreferences;
import com.bensoft.minesweeper.resources.FileResources;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;

/**
 *
 * @author Ben Wolsieffer
 */
public class Grid extends JPanel implements GUILoader {

    public static final int MAX_HEIGHT = 24;
    public static final int MIN_HEIGHT = 9;
    public static final int MAX_WIDTH = 30;
    public static final int MIN_WIDTH = 9;
    public static final int MAX_MINES = 667;
    public static final int MIN_MINES = 10;
    private ArrayList<ArrayList<GridSpace>> contents;
    private JFrame parent;
    private int clickedSpaces;

    public Grid(JFrame parent) {
        this.parent = parent;
        init();
    }

    private void init() {
        initializeComponents();
        setLayouts();
        addComponents();
        addComponentListeners();
        otherInit();
    }

    @Override
    public void initializeComponents() {
        contents = new ArrayList<>();
        rebuild(ApplicationPreferences.getWidth(), ApplicationPreferences.getHeight(), ApplicationPreferences.getMines());
    }

    @Override
    public void setLayouts() {
    }

    @Override
    public void addComponents() {
    }

    @Override
    public void addComponentListeners() {
    }

    @Override
    public void otherInit() {
        FileResources.addThemeChangeListener(new FileResources.ThemeChangeListener() {
            @Override
            public void themeChanged() {
                setBackground(FileResources.getColor("grid.color"));
            }
        });
        setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3, 7, 7, 7), BorderFactory.createBevelBorder(BevelBorder.LOWERED)));
    }

    /**
     * Resets the grid using the values stored in the preferences.
     */
    public void rebuild() {
        int width = ApplicationPreferences.getWidth(), height = ApplicationPreferences.getHeight(), mines = ApplicationPreferences.getMines();
        clickedSpaces = 0;
        contents.clear();
        int remainingMines = mines;
        int remainingSpaces = height * width;
        for (int y = 0; y < height; y++) {
            contents.add(new ArrayList<GridSpace>());
            for (int x = 0; x < width; x++) {
                if (Math.round(Math.random() * ((double) remainingSpaces)) < remainingMines) {
                    contents.get(y).add(new GridSpace(x, y, true));
                    remainingMines--;
                } else {
                    contents.get(y).add(new GridSpace(x, y));
                }
                remainingSpaces--;
            }
        }
        setLayout(new GridLayout(height, width));
        removeAll();
        int y = 0;
        for (ArrayList<GridSpace> c : contents) {
            int x = 0;
            for (GridSpace g : c) {
                byte number = 0;
                if (x != 0) {
                    if (contents.get(y).get(x - 1).isMine()) {
                        number++;
                    }
                }
                if (y != 0 && x != 0) {
                    if (contents.get(y - 1).get(x - 1).isMine()) {
                        number++;
                    }
                }
                if (x != 0 && y < contents.size() - 1) {
                    if (contents.get(y + 1).get(x - 1).isMine()) {
                        number++;
                    }
                }
                if (y != 0) {
                    if (contents.get(y - 1).get(x).isMine()) {
                        number++;
                    }
                }
                if (y < contents.size() - 1) {
                    if (contents.get(y + 1).get(x).isMine()) {
                        number++;
                    }
                }
                if (x < c.size() - 1) {
                    if (contents.get(y).get(x + 1).isMine()) {
                        number++;
                    }
                }
                if (y != 0 && x < c.size() - 1) {
                    if (contents.get(y - 1).get(x + 1).isMine()) {
                        number++;
                    }
                }
                if (x < c.size() - 1 && y < contents.size() - 1) {
                    if (contents.get(y + 1).get(x + 1).isMine()) {
                        number++;
                    }
                }
                g.setNumber(number);
                add(g);
                x++;
            }
            y++;
        }
        ((MinesweeperFrame) parent).getDisplayPanel().setRemainingMines(mines);
        //setPreferredSize(new Dimension(width * GridSpace.SIZE, height * GridSpace.SIZE));
        //setPreferredSize(new Dimension(width*GridSpace.SIZE+14, height*GridSpace.SIZE+10));
        parent.pack();
    }

    /**
     * Resets the grid using the specified parameters and saves those values to
     * the preferences.
     *
     * @param width The width of the grid
     * @param height The height of the grid
     * @param mines The number of mines
     */
    public void rebuild(int width, int height, int mines) {
        ApplicationPreferences.setWidth(width);
        ApplicationPreferences.setHeight(height);
        ApplicationPreferences.setMines(mines);
        rebuild();
    }

    private void processClick(GridSpace space, int dontCheckIndex) {
        switch (dontCheckIndex) {
            case 0:
                dontCheckIndex = 7;
                break;               //                   Don't check indices
            case 1:                  //           ___________ ___________ ___________
                dontCheckIndex = 6;  //          |           |           |           |
                break;               //          |           |           |           |
            case 2:                  //          |     2     |     4     |     7     |
                dontCheckIndex = 5;  //          |           |           |           |
                break;               //          |___________|___________|___________|
            case 3:                  //          |           |           |           |
                dontCheckIndex = 4;  //          |           |           |           |
                break;               //          |     1     |   space   |     6     |
            case 4:                  //          |           |           |           |
                dontCheckIndex = 3;  //          |___________|___________|___________|
                break;               //          |           |           |           |
            case 5:                  //          |           |           |           |
                dontCheckIndex = 2;  //          |     0     |     3     |     5     |
                break;               //          |           |           |           |
            case 6:                  //          |___________|___________|___________|
                dontCheckIndex = 1;
                break;
            case 7:
                dontCheckIndex = 0;
                break;
        }
        // If the space isn't a mine and it is blank, 
        if (!space.isMine() && space.getNumber() == 0) {
            int[] xAdd = {-1, -1, -1, 0, 0, 1, 1, 1};
            int[] yAdd = {1, 0, -1, 1, -1, 1, 0, -1};
            for (byte i = 0; i < 8; i++) {
                int yGet = space.getGridY() + yAdd[i];
                int xGet = space.getGridX() + xAdd[i];
                if (i != dontCheckIndex && yGet >= 0 && yGet < contents.size() && xGet >= 0 && xGet < contents.get(yGet).size()) {
                    GridSpace g = contents.get(yGet).get(xGet);
                    if (!g.isMine() && g.getState() != GridSpace.State.FLAG && g.getState() != GridSpace.State.LEFT_CLICKED) {
                        g.leftClick();
                        clickedSpaces++;
                        if (g.getNumber() == 0) {
                            processClick(g, i);
                        }
                    }
                }
            }
        } else if (space.isMine() && space.getState() != GridSpace.State.FLAG) {
            space.setBackground(Color.yellow);
            for (ArrayList<GridSpace> y : contents) {
                for (GridSpace x : y) {
                    if (x.isMine()) {
                        x.leftClick();
                    } else if (x.getState() == GridSpace.State.FLAG) {
                        x.setState(GridSpace.State.MINE_X);
                    }
                }
            }
            ApplicationController.getGameManager().loseGame();

        }
        if (clickedSpaces == (ApplicationPreferences.getWidth() * ApplicationPreferences.getHeight()) - ApplicationPreferences.getMines()) {
            for (ArrayList<GridSpace> y : contents) {
                for (GridSpace x : y) {
                    if (x.isMine()) {
                        x.setState(GridSpace.State.FLAG);
                    }
                }
            }
            repaint();
            ApplicationController.getGameManager().winGame();
        }
    }

    /**
     * Helper method that processes a click on a certain {@code GridSpace}.
     *
     * @param space The space that was clicked.
     */
    public void processClick(GridSpace space) {
//        System.out.println(contents.get(0).get(0).getWidth());
//        for(int y = 0; y<contents.size(); y++){
//            for(int x = 0; x<contents.get(y).size(); x++){
//                System.out.println("Preferred size: "+contents.get(y).get(x).getPreferredSize()+"; Actual size: "+contents.get(y).get(x).getSize()+"; y: "+y+"; x: "+x);
//            }
//        }
//        for (Component c : getComponents()) {
//            System.out.println("Class: " + c.getClass().getName() + "; Preferred size: " + c.getPreferredSize() + "; Actual size: " + c.getSize());
//        }
        if (!space.isMine()) {
            clickedSpaces++;
        }
        processClick(space, -1);
    }
}
